package al.xc.springboot.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 异常处理切面
 */
@Slf4j
@Aspect
@Component
public class ExceptionAspect {

    @Autowired
    private ITokenVerifier tokenVerifier;
    @Autowired
    private IReCallVerifier reCallVerifier;

    @Before("@annotation(al.xc.springboot.aop.ParamValidate)")
    @Order(1)
    public void beforeParam(JoinPoint joinPoint) throws Exception {
        ParamValidate validate = getParamValidate(joinPoint);
        tokenVerifier.verify(validate);

        if (null != validate) {
            doExceptionCheck(joinPoint);
        }
    }

    @Before("@annotation(al.xc.springboot.aop.RecallValidate)")
    @Order(2)
    public void beforeRecall(JoinPoint joinPoint) throws Exception {
        reCallVerifier.before(joinPoint);
    }

    @After("@annotation(al.xc.springboot.aop.RecallValidate)")
    public void afterRecall(JoinPoint joinPoint) throws Exception {
        reCallVerifier.after(joinPoint);
    }

    /**
     * 前置检查并抛出错误异常
     *
     * @param joinPoint
     * @throws Exception
     */
    private void doExceptionCheck(JoinPoint joinPoint) throws Exception {
        Object[] args = joinPoint.getArgs();
        if (null == args) {
            return;
        }
        throwsException(args);
    }

    /**
     * 获取方法上的自定义参数检测注解
     *
     * @param joinPoint
     * @return
     */
    private ParamValidate getParamValidate(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        return method.getAnnotation(ParamValidate.class);
    }

    /**
     * 查找错误并抛出异常
     *
     * @param args
     * @throws Exception
     */
    private void throwsException(Object[] args) throws Exception {
        for (Object arg : args) {
            if (!(arg instanceof BindingResult)) {
                continue;
            }
            BindingResult result = (BindingResult) arg;
            if (result.getErrorCount() <= 0) {
                continue;
            }
            List<ObjectError> listError = result.getAllErrors();
            String errorInfo = "";
            for (ObjectError error : listError) {
                if (error instanceof FieldError) {
                    FieldError fieldError = (FieldError) error;
                    errorInfo = String.format("%s : %s", fieldError.getField(), fieldError.getDefaultMessage());
                } else {
                    errorInfo = String.format("%s : %s", error.getCode(), error.getDefaultMessage());
                }
                log.warn(errorInfo);
                throw new Exception(errorInfo);
            }
        }
    }
}
